package com.itheima.easy.security;

import com.alibaba.fastjson.JSONObject;
import com.itheima.easy.constant.SecurityConstant;
import com.itheima.easy.constant.UserCacheConstant;
import com.itheima.easy.utils.EmptyUtil;
import com.itheima.easy.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @ClassName JwtAuthorizationManager.java
 * @Description 授权管理器
 */
@Slf4j
@Component
public class JwtAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    JwtTokenManager jwtTokenManager;

    @Autowired
    RedissonClient redissonClient;

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication,
                                       RequestAuthorizationContext requestAuthorizationContext) {
        //用户当前请求路径
        String method = requestAuthorizationContext.getRequest().getMethod();
        String requestURI = requestAuthorizationContext.getRequest().getRequestURI();
        String targetUrl = (method+requestURI);
        //获得请求中的认证后传递过来的userToken
        String userToken = requestAuthorizationContext.getRequest().getHeader(SecurityConstant.USER_TOKEN);
        //如果userToken为空,则当前请求不合法
        if (EmptyUtil.isNullOrEmpty(userToken)){
            return new AuthorizationDecision(false);
        }
        //通过userToken获取jwtToken
        String jwtTokenKey = UserCacheConstant.JWT_TOKEN+userToken;
        RBucket<String> jwtTokenKeyBucket = redissonClient.getBucket(jwtTokenKey);
        String jwtToken = jwtTokenKeyBucket.get();
        //如果jwtToken为空,则当前请求不合法
        if (EmptyUtil.isNullOrEmpty(jwtToken)){
            return new AuthorizationDecision(false);
        }
        //校验jwtToken是否合法
        boolean verifyToken = jwtTokenManager.isVerifyToken(jwtToken);
        if (!verifyToken){
            return new AuthorizationDecision(false);
        }
        ///如果校验jwtToken通过，则获得userVo对象
        UserVo userVo = JSONObject.parseObject(String.valueOf(jwtTokenManager.getCurrentUser(jwtToken)),UserVo.class);

        //用户剔除校验:redis中最新的userToken与出入的userToken不符合，则认为当前用户被后续用户剔除
        RBucket<String> UserTokenBucket = redissonClient
            .getBucket(UserCacheConstant.USER_TOKEN + userVo.getUsername());
        String currentUserToken = UserTokenBucket.get();
        if (!userToken.equals(currentUserToken)){
            return new AuthorizationDecision(false);
        }
        //如果当前UserToken存活时间少于10分钟，则进行续期
        long remainTimeToLive = UserTokenBucket.remainTimeToLive();
        if (remainTimeToLive<=6000000){
            //续期处理：使用userToken与重新构建jwtToken进行关联
            Map<String,Object> claims = new HashMap<>();
            claims.put("currentUser",JSONObject.toJSONString(userVo));
            //重新构建jwtToken
            jwtToken = jwtTokenManager.issuedToken("system",
                jwtTokenManager.getJwtProperties().getTtl(), userToken, claims);
            long ttl = Long.valueOf(jwtTokenManager.getJwtProperties().getTtl())/1000;
            jwtTokenKeyBucket.set(jwtToken,ttl, TimeUnit.SECONDS);
        }

        //当前用户资源是否包含当前URL
        for (String resourceRequestPath : userVo.getResourceRequestPaths()) {
            boolean isMatch = antPathMatcher.match(resourceRequestPath, targetUrl);
            if (isMatch){
                log.info("用户:{}拥有targetUrl权限:{}==========",userVo.getUsername(),targetUrl);
                return new AuthorizationDecision(true);
            }
        }

        return new AuthorizationDecision(false);
    }
}
